﻿using System.Collections.Generic;
using System.Data;
using System.Reflection;

namespace System
{
    /// <summary>
    /// 表示为集合实例的扩展。
    /// </summary>
    public static class CollectionExtension
    {
        #region Merge

        ///// <summary>
        ///// 若指定键不存在字典中，则将该键值对添加到字典中；否则不进行任何操作。
        ///// </summary>
        ///// <typeparam name="TKey">字典中键的类型。</typeparam>
        ///// <typeparam name="TValue">字典中值的类型。</typeparam>
        ///// <param name="dictionary">被扩展的IDictionary实例。</param>
        ///// <param name="key">用作要添加元素的键的对象。</param>
        ///// <param name="value">用作要添加元素的值的对象。</param>
        ///// <returns>若键已存在，则为 true；否则为 false。</returns>
        //public static bool AddIfNotContains<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value)
        //{
        //    if (!dictionary.ContainsKey(key))
        //    {
        //        dictionary.Add(key, value);
        //        return true;
        //    }
        //    return false;
        //}

        ///// <summary>
        ///// 合并指定键中相同的值，若该键不存在，则抛出 <see cref="KeyNotFoundException" /> 异常。
        ///// </summary>
        ///// <typeparam name="TKey">字典中键的类型</typeparam>
        ///// <param name="dictionary">被扩展的IDictionary实例。</param>
        ///// <param name="key">用作要合并的元素的键的对象。</param>
        ///// <param name="value">作为要合并的值。</param>
        ///// <exception cref="ArgumentNullException">key</exception>
        ///// <exception cref="KeyNotFoundException">字典中的键无法找到。</exception>
        //public static void Merge<TKey>(this IDictionary<TKey, object> dictionary, TKey key, object value)
        //{
        //    if (key == null)
        //    {
        //        throw new ArgumentNullException(nameof(key), "指定的键不能使 null 值。");
        //    }

        //    if (!dictionary.ContainsKey(key))
        //    {
        //        throw new KeyNotFoundException("无法找到键 {0}。".StringFormat(key));
        //    }
        //    dictionary[key] = string.Concat(dictionary[key], value);
        //}

        ///// <summary>
        ///// 若指定字典中的键存在，则将值进行合并；否则将该键值对添加到字典中。
        ///// </summary>
        ///// <typeparam name="TKey">字典中键的类型</typeparam>
        ///// <param name="dictionary">被扩展的IDictionary实例。</param>
        ///// <param name="key">用作要添加或合并元素的键的对象。</param>
        ///// <param name="value">用作要添加或合并元素的值。</param>
        ///// <exception cref="ArgumentNullException">key</exception>
        //public static void AddOrMerge<TKey>(this IDictionary<TKey, object> dictionary, TKey key, object value)
        //{
        //    if (!dictionary.AddIfNotContains(key, value))
        //    {
        //        dictionary.Merge(key, value);
        //    }
        //}
        #endregion

        #region GetOrDefault
        /// <summary>
        /// 获取字典中的值并转换成期望值，当指定字典中不存在指定键时，返回默认类型的默认值。
        /// </summary>
        /// <typeparam name="TKey">字典中键的类型</typeparam>
        /// <typeparam name="TValue">字典中值的类型</typeparam>
        /// <param name="dictionary"><see cref="IDictionary{TKey, TValue}"/>类的实例扩展。</param>
        /// <param name="key">在字典中存在的键。</param>
        /// <param name="defaultValue">当字典键中不存在时设置的默认值。</param>
        /// <returns>若字典中的键存在，则返回指定字典值的指定类型，否则返回指定类型的默认值。</returns>
        public static TValue GetOrDefault<TKey, TValue>(this IDictionary<TKey, object> dictionary, TKey key, TValue defaultValue)
        {
            if (dictionary.TryGetValue(key, out object value))
            {
                return value.To(defaultValue);
            }

            return defaultValue;
        }

        /// <summary>
        /// 获取字典中的值并转换成期望值，当指定字典中不存在指定键时，返回默认类型的默认值。
        /// </summary>
        /// <typeparam name="TKey">字典中键的类型</typeparam>
        /// <typeparam name="TValue">字典中值的类型</typeparam>
        /// <param name="dictionary"><see cref="IDictionary{TKey, TValue}"/>类的实例扩展。</param>
        /// <param name="key">在字典中存在的键。</param>
        /// <returns>若字典中的键存在，则返回指定字典值的指定类型，否则返回指定类型的默认值。</returns>
        public static TValue GetOrDefault<TKey, TValue>(this IDictionary<TKey, object> dictionary, TKey key) => dictionary.GetOrDefault(key, default(TValue));        
        #endregion

        #region ForEach
        /// <summary>
        /// 对当前的集合进行遍历。
        /// </summary>
        /// <typeparam name="T">遍历的类型。</typeparam>
        /// <param name="enumerable">当前集合的实例扩展。</param>
        /// <param name="action">在遍历中的操作委托。该委托接受一个参数，表示当前遍历的对象。</param>
        /// <exception cref="System.ArgumentNullException">action</exception>
        /// <example>
        ///   <c>list.ForEach(item=&gt;
        /// {
        /// //to do...
        /// });</c>
        /// </example>
        public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
        {
            if (action == null)
            {
                throw new ArgumentNullException(nameof(action));
            }
            foreach (var item in enumerable)
            {
                action(item);
            }
        }
        #endregion

        /// <summary>
        /// 将当前集合泛型对象转换成相同数据表结构的 DataTable 对象。
        /// </summary>
        /// <typeparam name="T">集合实体类型。</typeparam>
        /// <param name="source">要转换的实体类型。</param>
        /// <param name="tableName">DataTable 的表名称。</param>
        /// <returns>与实体集合的结构相同的 DataTable 数据表对象。</returns>
        /// <exception cref="ArgumentNullException">source 不能是 null。</exception>
        public static DataTable ToDataTable<T>(this IEnumerable<T> source, string tableName = "Table")
            where T : class, new()
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            var datatable = new DataTable(tableName);
            
            foreach (var property in Activator.CreateInstance<T>().GetType().GetProperties())
            {
                var column = new DataColumn(property.Name, property.PropertyType);

                if (!datatable.Columns.Contains(property.Name))
                {
                    datatable.Columns.Add(column);
                }
            }

            source.ForEach(item =>
            {
                var row = datatable.NewRow();
                item.GetType().GetProperties().ForEach(property =>
                {
                    if (property.CanRead)
                    {
                        row[property.Name] = property.GetValue(item);
                    }
                });
                datatable.Rows.Add(row);
            });
            return datatable;
        }

        /// <summary>
        /// 将当前的 DataTable 对象转换成相同结构的实体集合。
        /// </summary>
        /// <typeparam name="T">集合实体类型。</typeparam>
        /// <param name="dataTable">一个数据表对象。</param>
        /// <returns>与数据表对象结构相同的实体集合。</returns>
        /// <exception cref="ArgumentNullException">dataTable 不能是 null。</exception>
        public static List<T> ToList<T>(this DataTable dataTable)
            where T:class,new()
        {
            if (dataTable == null)
            {
                throw new ArgumentNullException(nameof(dataTable));
            }

            var list = new List<T>();

            foreach (DataRow row in dataTable.Rows)
            {
                T item = Activator.CreateInstance<T>();

                foreach (var property in item.GetType().GetProperties())
                {
                    if (!property.CanWrite)
                    {
                        continue;
                    }

                    DataColumn column = dataTable.Columns[property.Name];
                    if (column == null)
                    {
                        continue;
                    }
                    var value= Convert.ChangeType(row[property.Name], property.PropertyType);

                    property.SetValue(item, value);
                }
                list.Add(item);
            }
            return list;
        }

    }
}
